package com.anp.business.utils;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import javax.net.ssl.*;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;

/**
 * @Name AxinConnection
 * 
 * 
 * @author SoulW
 */
public class AxinConnection {
	
	// Const
	private static final Logger logger = Logger.getLogger(AxinConnection.class);
	private static final String CONST_ENCODE = "UTF-8";
	private static final int CONST_CONN_TIMEOUT = 25000;
	private static final int CONST_READ_TIMEOUT = 25000;
	private static final String CONST_GET = "GET";
	private static final String CONST_POST = "POST";
	private static SSLContext context;

	// Field
	private HttpURLConnection conn;
	private InputStream is;
	private OutputStream out;
	private ByteArrayOutputStream bOut;
	
	/**
	 * Constructor.
	 */
	public AxinConnection() {

	}
	
	/**
	 * Constructor.
	 * 
	 * @param url
	 * @throws Exception If the url is a error data.
	 */
	public AxinConnection(String url) throws Exception {

		logger.info(String.format("The request url is (%s)", url));
		this.conn = (HttpURLConnection) new URL(url).openConnection();
		initializeConn();
	}
	
	/**
	 * Constructor.
	 * 
	 * @param conn
	 */
	public AxinConnection(HttpURLConnection conn) throws Exception {

		logger.info(String.format("The request path is (%s)", conn.getURL().getPath()));
		this.conn = conn;
		initializeConn();
	}
	
	/**
	 * For test design pj.
	 */
	private void setTimeout() {
		
		conn.setConnectTimeout(CONST_CONN_TIMEOUT);
		conn.setReadTimeout(CONST_READ_TIMEOUT);
	}
	
	/**
	 * Post only with the data.
	 * 
	 * @param data
	 * @return
	 */
	public String postOnly(String data) {
		
		String rs = null;
		
		try {
			
			if (this.conn == null)
				throw new Exception("The connection object must not be null.");
			
			logger.info(String.format("The post data is (%s)", data));
			this.out = conn.getOutputStream();
			this.out.write(data.getBytes(CONST_ENCODE));
			this.out.flush();
			
			this.is = conn.getInputStream();
			rs = readInputStream(null);
			logger.info(String.format("The post response data is (%s)", rs));
			
			return rs;
		} catch (Exception e) {
			try{
				logger.error(String.format("The post is failtured and data is (%s).", data), e);
				Map<String, String> headers = URLConnectionHeaderUtil.printResponseHeader(this.conn);
				if ("HTTP/1.1 412".equals(headers.get(null))) {
					return "HTTP/1.1 412";
				}
			}catch(Exception e1){
			    logger.error(e1.getMessage(),e1);
			}
			return "";
		} finally {
			try {
				if (this.out != null)
					out.close();
				
				if (this.is != null)
					is.close();
				
				if (this.conn != null)
					conn.disconnect();
				
				conn = null;
			} catch (Exception e) {
				
				logger.error(String.format("The resource(%s) closed failture.", conn.getURL().getHost()), e);
			}
		}
	}
	
	/**
	 * Post url.
	 * 
	 * @param data
	 * @param url
	 * @return
	 */
	public String postUrl(String url, String data) throws Exception {

		this.conn = (HttpURLConnection) new URL(url).openConnection();
		initializeConn();
		
		return postOnly(data);

	}

	/**
	 * Post url by change he
	 *
	 * @param data
	 * @param url
	 * @return
	 */
	public String postUrl(String url, String data,Map<String, String> headers) throws Exception {
		this.conn = (HttpURLConnection) new URL(url).openConnection();
		initializeConn(CONST_POST,headers);
		return postOnly(data);
	}
	
	/**
	 * Read the input stream.
	 */
	private String readInputStream(String charset) throws Exception {
		
		String rs = "";
		bOut = new ByteArrayOutputStream();
		for (int b = -1;;) {
			
			b = is.read();
			if (b == -1)
				break;
			
			bOut.write(b);
		}
		
		bOut.flush();
		if (StringUtils.isBlank(charset)) {
			rs = new String(bOut.toByteArray(), CONST_ENCODE);
		}else {
			rs = new String(bOut.toByteArray(), charset);
		}
		bOut.close();
		return rs;
	}
	
	/**
	 * Get url
	 * 
	 * @param url
	 * @param headers
	 * @return
	 */
	public String getUrl(String url, Map<String, String> headers,String charset) throws Exception {
		String rs = null;
		conn = (HttpURLConnection) new URL(url).openConnection();
		//设置响应头
		initializeConn(CONST_GET, headers);
		Map<String, List<String>> headerFields = conn.getHeaderFields();
		for(String key : headerFields.keySet()){
			logger.info(String.format("key : %s, value : %s", key, headerFields.get(key)));
		}
		try {
			if (this.conn == null)
				throw new Exception("The connection object must not be null.");
			this.is = conn.getInputStream();
			rs = readInputStream(charset);
			logger.info(String.format("The post response data is (%s)", rs));
		} catch (Exception e) {
			logger.error("The get is failtured", e);
			return "";
		}finally{
			try {
				if(this.out != null){
					out.close();
				}
				if(this.is != null){
					is.close();
				}
				if(this.conn != null){
					conn.disconnect();
				}
			} catch (Exception e) {
				logger.error(String.format("The resource(%s) closed failture.", conn.getURL().getHost()), e);
			}
		}
		return rs;
	}
	
	/**
	 * Initialize connection for urlconnection.
	 */
	private void initializeConn() throws Exception {
	
		conn.setDoInput(true);
		conn.setDoOutput(true);
		setTimeout();
		conn.setRequestMethod("POST");
		conn.setRequestProperty("Host", conn.getURL().getHost());
		conn.setRequestProperty("Accept", "text/html; charset=utf-8");
		conn.setRequestProperty("Connection", "close");
		conn.setRequestProperty("Cache-Control", "no-cache");
		conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
		conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36");
		conn.connect();
	}

	private void initializeConn(String method, Map<String, String> headers) throws Exception{
		if(CONST_POST.equals(method)){
			conn.setDoInput(true);
			conn.setDoOutput(true);
		}
		setTimeout();
		boolean isSetContentType= false;
		if(headers != null){
			for(String key : headers.keySet()){
				if ("Content-Type".equals(key)) {
					isSetContentType=true;
				}
				if ("Authorization".equals(key)) {
					conn.addRequestProperty(key, headers.get(key));
					continue;
				}
				conn.setRequestProperty(key, headers.get(key));
			}
		}
		//  如果没设置过请求头 ， 就设置默认的
		if (!isSetContentType){
			conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
			conn.setRequestProperty("Accept", "text/html; charset=utf-8");
		}
		conn.setUseCaches(false);
		conn.setRequestMethod(method);
		conn.setRequestProperty("Host", conn.getURL().getHost());
		conn.setRequestProperty("Connection", "close");
		conn.setRequestProperty("Cache-Control", "no-cache");
		conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36");
		conn.connect();
	}
	
	
	static {
		try {
			context = SSLContext.getInstance("SSL");
			context.init(new KeyManager[]{}, new TrustManager[]{new X509TrustManager() {
				@Override
				public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
				}
				@Override
				public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
				}
				@Override
				public X509Certificate[] getAcceptedIssuers() {
					return null;
				}
			}}, null);
			HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
			HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
				@Override
				public boolean verify(String arg0, SSLSession arg1) {
					return true;
				}
			});
		} catch (Exception e) {
			logger.error("SSL init err...", e);
		}
	}
	
}
